package main

import (
    "fmt"
    "os"
    "strings"
    "time"
)

// LogEntry 日志条目接口
type LogEntry interface {
    Format() string
    GetLevel() string
    GetMessage() string
    GetTimestamp() time.Time
}

// LogHandler 日志处理器接口（抽象创建者）
type LogHandler interface {
    CreateLogEntry(level string, message string, metadata map[string]interface{}) LogEntry
    HandleLog(entry LogEntry) error
    SetLogLevel(level string)
}

// SimpleLogEntry 简单日志条目
type SimpleLogEntry struct {
    Level     string
    Message   string
    Timestamp time.Time
}

func (s *SimpleLogEntry) Format() string {
    return fmt.Sprintf("[%s] %s - %s", 
        s.Level, 
        s.Timestamp.Format("2006-01-02 15:04:05"), 
        s.Message)
}

func (s *SimpleLogEntry) GetLevel() string {
    return s.Level
}

func (s *SimpleLogEntry) GetMessage() string {
    return s.Message
}

func (s *SimpleLogEntry) GetTimestamp() time.Time {
    return s.Timestamp
}

// StructuredLogEntry 结构化日志条目
type StructuredLogEntry struct {
    Level     string
    Message   string
    Timestamp time.Time
    Fields    map[string]interface{}
}

func (s *StructuredLogEntry) Format() string {
    var fields []string
    for k, v := range s.Fields {
        fields = append(fields, fmt.Sprintf("%s=%v", k, v))
    }
    return fmt.Sprintf("[%s] %s - %s | %s", 
        s.Level, 
        s.Timestamp.Format("2006-01-02 15:04:05"), 
        s.Message,
        strings.Join(fields, ", "))
}

func (s *StructuredLogEntry) GetLevel() string {
    return s.Level
}

func (s *StructuredLogEntry) GetMessage() string {
    return s.Message
}

func (s *StructuredLogEntry) GetTimestamp() time.Time {
    return s.Timestamp
}

// ConsoleLogHandler 控制台日志处理器
type ConsoleLogHandler struct {
    minLevel string
}

func (c *ConsoleLogHandler) CreateLogEntry(level string, message string, metadata map[string]interface{}) LogEntry {
    return &SimpleLogEntry{
        Level:     level,
        Message:   message,
        Timestamp: time.Now(),
    }
}

func (c *ConsoleLogHandler) HandleLog(entry LogEntry) error {
    if c.shouldLog(entry.GetLevel()) {
        fmt.Println(entry.Format())
    }
    return nil
}

func (c *ConsoleLogHandler) SetLogLevel(level string) {
    c.minLevel = level
}

func (c *ConsoleLogHandler) shouldLog(level string) bool {
    levels := map[string]int{"DEBUG": 0, "INFO": 1, "WARN": 2, "ERROR": 3}
    return levels[level] >= levels[c.minLevel]
}

// FileLogHandler 文件日志处理器
type FileLogHandler struct {
    filename string
    minLevel string
}

func (f *FileLogHandler) CreateLogEntry(level string, message string, metadata map[string]interface{}) LogEntry {
    return &StructuredLogEntry{
        Level:     level,
        Message:   message,
        Timestamp: time.Now(),
        Fields:    metadata,
    }
}

func (f *FileLogHandler) HandleLog(entry LogEntry) error {
    if f.shouldLog(entry.GetLevel()) {
        file, err := os.OpenFile(f.filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
        if err != nil {
            return err
        }
        defer file.Close()
        
        _, err = file.WriteString(entry.Format() + "\n")
        return err
    }
    return nil
}

func (f *FileLogHandler) SetLogLevel(level string) {
    f.minLevel = level
}

func (f *FileLogHandler) shouldLog(level string) bool {
    levels := map[string]int{"DEBUG": 0, "INFO": 1, "WARN": 2, "ERROR": 3}
    return levels[level] >= levels[f.minLevel]
}

// 使用演示
func main() {
    fmt.Println("=== 日志处理器工厂演示 ===")
    
    // 创建不同类型的日志处理器
    consoleHandler := &ConsoleLogHandler{minLevel: "INFO"}
    fileHandler := &FileLogHandler{filename: "app.log", minLevel: "DEBUG"}
    
    handlers := []LogHandler{consoleHandler, fileHandler}
    
    for _, handler := range handlers {
        metadata := map[string]interface{}{
            "user_id": "12345",
            "action":  "login",
        }
        
        entry := handler.CreateLogEntry("INFO", "用户登录成功", metadata)
        handler.HandleLog(entry)
        
        errorEntry := handler.CreateLogEntry("ERROR", "数据库连接失败", metadata)
        handler.HandleLog(errorEntry)
    }
}